# The World'Const
nil = None
true = True
false = False

# The King'Heart
# Any λab... to λa->λb->[λ]...
def Curry(f:callable,*a,**b):
    flen = f.__code__.co_argcount-len(f.__defaults__ or ())
    def part(*x,**y):
        if len(a)+len(x)+len(b)+len(y) < flen:
            return Curry(f,*a,*x,**b,**y)
        else:
            return f(*a,*x,**b,**y)
    return part

# The King'Eye
# Partial (Remove Left)
# λa[] -> λ[]
def Partl(f:callable,*a,**b):
    return lambda *x,**y:f(*a,*x,**b,**y)
# Partial (Remove Right)
# λa[] -> λa
def Partr(f:callable,*a,**b):
    return lambda *x,**y:f(*x,*a,**y,**b)

# The King'Hand
# λb~c λa~b λ[]~a ... -> λ[]~?
# λb~c λa~b λ[]~a -> λ[]~c
# λa~b λ[]~a -> λ[]~b
def Compose(b:callable,*a:callable):
    assert len(a)
    if len(a) > 1:
        return Compose(b,Compose(*a))
    return lambda *x,**y:b(a[0](*x,**y))

# The Kind'Soul
# x -> x
Id = lambda x:x

# The Three'Sword
# λba~b [a] -> b
from functools import reduce
@Curry
def Reduce(f:callable,x:iter):return reduce(f,x)
# λa~b [a] -> [b]
@Curry
def Map(f:callable,x:iter):return map(f,x)
# λa~Bool [a] -> [a]
@Curry
def Filter(f:callable,x:iter):return filter(f,x)

# The God'Sea
# λa~b λb~c ... -> λa~?
# λa~b λb~c -> λa~c
# λa~b -> λa~b
def Pipeline(f1:callable,*fs:callable):
    return lambda a:reduce(lambda x,f:f(x),(a,f1,*fs))

import itertools
# The Ghost'Attack
Repeat = itertools.repeat

# The Bug'Fix on Python
class Tailcall:
    __slots__ = ("a","b")
    def __init__(me,*a,**b):
        me.a = a
        me.b = b
def UseTailcall(f):
    """
    Usage:
    @UseTailcall
    def SumN(n,a=0):
        if n<1: return a
        else:   return Tailcall(n-1,a+n)
    SumN(10) #55
    """
    def Loop(*a,**b):
        x = Tailcall(*a,**b)
        while isinstance(x,Tailcall):
            x = f(*x.a,**x.b)
        return x
    return Loop

# The Demon'Box
class Monad:
    __slots__ = ("__x",)
    # Box and Auto-join
    # Monad:a -> Monad:a
    # a -> Monad:a
    def __init__(me,x):
        me.__x = ~x if isinstance(x,type(me)) else x
    # Get from Box
    # Monad:a -> a
    def __invert__(me):
        return me.__x

    # FlatMap/Map
    # Monad:a λa~b -> Monad:b
    # Monad:a λa~Monad:b -> Monad:b
    # Monad:a Monad:λa~b -> Monad:b
    def __sub__(me,f:callable):
        b = f(~me)
        return b if isinstance(b,Monad) else (type(me))(b)

    # Monad:a Monad:b -> Monad:b
    def __and__(me,b):
        return me-(lambda _:b)

    # As Function
    # Monad:λ[]~b [] -> b
    def __call__(me,*a,**b):
        return (~me)(*a,**b)

    # Applicative
    # Monad:λa~b Monad:a -> Monad:b
    def Apply(me,a):
        return (type(me))((~me)(~a))

# The World'Select
class Maybe(Monad):
    def __Null():pass
    # a -> Maybe:a
    #   -> Maybe:Null
    def __init__(me,x=__Null):
        super().__init__(x)
    # Maybe:a λa~b -> Maybe:b
    # Maybe:Null λa~b -> Maybe:Null
    def __sub__(me,f:callable):
        return me if ~me is Maybe.__Null else super().__sub__(f)

# The World'Line
class Either(Monad):pass
class EitherOver(Either):
    def __sub__(me,f:callable):
        return me
    def __and__(me,b:Monad):
        return b
    def __call__(me,*a,**b):
        return me
    def Apply(me,a:Monad):
        return me
class EitherNext(Either):pass

# The Demon'Door
class IO(Monad):
    # λa~b -> IO:b
    def __init__(me,f:callable):
        super().__init__(f)
    # IO:a λa~IO:b -> IO:b
    # IO:a λa~b -> IO:b
    # IO:a IO:λa~b -> IO:b
    def __sub__(me,f:callable):
        return IO(Compose(f,~me))
    # IO:a IO:b -> IO:b
    def __and__(me,b):
        def f():
            me()
            return b()
        return IO(f)

